/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is Forte for Java, Community Edition. The Initial
* Developer of the Original Code is Sun Microsystems, Inc. Portions
* Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
*/
package org.netbeans.core.awt;
import java.awt.*;
import java.awt.event.*;
import java.util.Enumeration;
import java.util.ResourceBundle;
import java.util.Vector;
import javax.swing.*;
import javax.swing.border.*;
import org.openide.util.NbBundle;
/** The ButtonBar is a bar that contains buttons.
* The button can be oriented both horizontally (default) or vertically.
* There are two groups of buttons - the left(bottom) group is aligned to the
* left(bottom) edge of the ButtonBar and the right(top) group is aligned to the
* right(top) edge (you would probably guess so, would not you?).
* The buttons are currently identified by strings - the two groups
* are passed to the ButtonBar in the constructor and the ButtonBar fires
* a ActionEvent with the name of the button as the ActionCommand.
*
* @author Ian Formanek, Petr Hamernik
* @version 0.35, May 31, 1998
*/
public class ButtonBar extends JPanel {
/** generated Serialized Version UID */
static final long serialVersionUID = -5909692079369563652L;
/** Constant for a preset configuration with no buttons */
public static final int EMPTY = 0;
/** Constant for a preset configuration with OK and Canel buttons */
public static final int OK_CANCEL = 1;
/** Constant for a preset configuration with Close button */
public static final int CLOSE = 2;
/** Constant for a preset configuration with Yes and No buttons */
public static final int YES_NO = 3;
/** Constant for a preset configuration with Yes, No and Cancel buttons */
public static final int YES_NO_CANCEL = 4;
/** Constant for a preset configuration with Details and Close buttons */
public static final int DETAILS_CLOSE = 5;
/** Default color of the text of the buttons */
public static final Color DEFAULT_FOREGROUND = new Color(200, 200, 255);
/** Default color of the button bar background */
public static final Color DEFAULT_BACKGROUND = Color.blue.darker().darker();
/** Default font of the text of the button */
public static final Font DEFAULT_FONT = new Font("SansSerif", Font.PLAIN, 18); // NOI18N
/** Constant for Single-side button mode - aligned to the right (top) */
public static final int SINGLE_RIGHT = 0;
/** Constant for Single-side button mode - aligned to the left (bottom) */
public static final int SINGLE_LEFT = 1;
/** Constant for Single-side button mode - centered */
public static final int SINGLE_CENTER = 2;
/** Default border between buttons */
public static final int BUTTONS_GAP = 6;
public static final int SECTIONS_GAP = 40;
/** Orientation constants */
public static final int HORIZONTAL = 0; // left to right orientation
public static final int VERTICAL = 1; // up to bottom orientation
/** Default button in the button bar */
private ButtonBarButton defaultButton;
/** Does the peer exist? It's modified in add/removeNotify */
private boolean peerExist;
/** Orientation of the button bar */
private int orientation;
/** Constructs an empty button bar. */
public ButtonBar() {
this(EMPTY);
}
/** Constructs a new button bar in given preset configuration.
* @param preset desired preset configuration (OK_CANCEL, CLOSE, ...)
*/
public ButtonBar(int preset) {
this(preset, HORIZONTAL);
}
/** Constructs a new button bar in given preset configuration.
* @param preset desired preset configuration (OK_CANCEL, CLOSE, ...)
* @param orientation buttons orientation (HORIZONTAL, VERTICAL)
*/
public ButtonBar(int preset, int orientation) {
this.orientation = orientation;
String[] left = null;
String[] right = null;
String def = ""; // NOI18N
ResourceBundle bundle = NbBundle.getBundle(ButtonBar.class);
switch (preset) {
case OK_CANCEL:
right = new String[2];
right[0] = bundle.getString("OKButton");
right[1] = bundle.getString("CancelButton");
def = right[0];
break;
case CLOSE:
right = new String[1];
right[0] = bundle.getString("CloseButton");
def = right[0];
break;
case YES_NO:
right = new String[2];
right[0] = bundle.getString("YesButton");
right[1] = bundle.getString("NoButton");
def = right[0];
break;
case YES_NO_CANCEL:
right = new String[3];
right[0] = bundle.getString("YesButton");
right[1] = bundle.getString("NoButton");
right[2] = bundle.getString("CancelButton");
def = right[0];
break;
case DETAILS_CLOSE:
left = new String[1];
left[0] = bundle.getString("DetailsButton");
right = new String[1];
right[0] = bundle.getString("CloseButton");
def = right[0];
break;
default:
case EMPTY: break;
}
initButtons(left, right, def);
}
/** Constructs a new button bar with buttons with specified labels.
* @param left an array of labels for the buttons to be aligned to the left (bottom)
* @param right an array of labels for the buttons to be aligned to the right (top)
*/
public ButtonBar(String[] left, String[] right) {
this(left, right, "", HORIZONTAL); // NOI18N
}
/** Constructs a new button bar with buttons with specified labels.
* @param left an array of labels for the buttons to be aligned to the left (bottom)
* @param right an array of labels for the buttons to be aligned to the right (top)
* @param defaultButton Label of the button which should be default.
*/
public ButtonBar(String[] left, String[] right, String defaultButton) {
this(left, right, defaultButton, HORIZONTAL);
}
/** Constructs a new button bar with buttons with specified labels.
* @param left an array of labels for the buttons to be aligned to the left (bottom)
* @param right an array of labels for the buttons to be aligned to the right (top)
* @param defaultButton Label of the button which should be default.
* @param orientation buttons orientation (HORIZONTAL, VERTICAL)
*/
public ButtonBar(String[] left, String[] right, String defaultButton, int orientation) {
this.orientation = orientation;
initButtons(left, right, defaultButton);
}
/** Constructs a new button bar with specified buttons.
* @param left an array of buttons to be aligned to the left (bottom)
* @param right an array of buttons to be aligned to the right (top)
*/
public ButtonBar(ButtonBarButton[] left, ButtonBarButton[] right) {
this(left, right, HORIZONTAL);
}
/** Constructs a new button bar with specified buttons and orientation.
* @param left an array of buttons to be aligned to the left (bottom)
* @param right an array of buttons to be aligned to the right (top)
* @param orientation buttons orientation (HORIZONTAL, VERTICAL)
*/
public ButtonBar(ButtonBarButton[] left, ButtonBarButton[] right, int orientation) {
this.orientation = orientation;
initButtons(left, right);
}
/** Constructs a new button bar with specified buttons aligned to the right. (top)
* @param right an array of buttons to be aligned to the right (top)
*/
public ButtonBar(ButtonBarButton[] right) {
this(null, right, HORIZONTAL);
}
/** Inistializes the buttons - called from the string constructors.
* @param left an array of labels for the buttons to be aligned to the left (bottom)
* or null if buttons should be placed to the right (top)
* @param right an array of labels for the buttons to be aligned to the right (top)
* or null if buttons should be placed to the left (bottom)
* @param defaultButton Label of the button which should be default.
*/
private void initButtons(String[] left, String[] right, String defaultButton) {
ButtonBarButton[] lefts = null;
ButtonBarButton[] rights = null;
if (left != null) {
lefts = new ButtonBarButton[left.length];
for (int i=0; i<left.length; i++) {
lefts[i] = new ButtonBarButton(left[i]);
if (left[i].equals(defaultButton))
setDefaultButton(lefts[i]);
}
}
if (right != null) {
rights = new ButtonBarButton[right.length];
for (int i=0; i<right.length; i++) {
rights[i] = new ButtonBarButton(right[i]);
if (right[i].equals(defaultButton))
setDefaultButton(rights[i]);
}
}
initButtons(lefts, rights);
}
/** Initializes the buttons - called from the constructor that takes the buttons
* and from the initButtons(String, String).
* @param left an array of buttons to be aligned to the left (bottom) or null if no
* buttons should be placed to the left (bottom)
* @param right an array of buttons to be aligned to the right (top) or null if no
* buttons should be placed to the right (top)
*/
private void initButtons(ButtonBarButton[] left, ButtonBarButton[] right) {
// setFont(DEFAULT_FONT);
// setForeground(DEFAULT_FOREGROUND);
// setBackground(DEFAULT_BACKGROUND);
setBorder(new EmptyBorder(6, 7, 6, 7));
setLayout(new BorderLayout());
JPanel inside = new JPanel();
inside.setLayout(new ButtonBarLayout());
if ((left == null) || (left.length == 0))
leftButtons = new ButtonBarButton[0];
else {
leftButtons = new ButtonBarButton[left.length];
for (int i=0; i<left.length; i++) {
leftButtons[i] = left[i];
leftButtons[i].attachButton(this);
inside.add(leftButtons[i]);
}
}
if ((right == null) || (right.length == 0))
rightButtons = new ButtonBarButton[0];
else {
rightButtons = new ButtonBarButton[right.length];
for (int i=0; i<right.length; i++) {
rightButtons[i] = right[i];
rightButtons[i].attachButton(this);
inside.add(rightButtons[i]);
}
}
add(inside, "Center"); // NOI18N
}
/** Sets the buttons of the ButtonBar.
* @param left The buttons to be placed on the left
* @param right The buttons to be placed on the right
*/
public void setButtons(ButtonBarButton[] left, ButtonBarButton[] right) {
setButtons(left, right, HORIZONTAL);
}
/** Sets the buttons of the ButtonBar.
* @param left The buttons to be placed on the left
* @param right The buttons to be placed on the right
* @param orientation buttons orientation (HORIZONTAL, VERTICAL)
*/
public void setButtons(ButtonBarButton[] left, ButtonBarButton[] right,
int orientation) {
this.orientation = orientation;
initButtons(left, right);
invalidate();
repaint();
}
/** Sets the buttons of the ButtonBar.
* @param left The String captions of the buttons to be placed on the left
* @param right The String captions of the buttons to be placed on the right
*/
public void setButtons(String[] left, String[] right) {
setButtons(left, right, HORIZONTAL);
}
/** Sets the buttons of the ButtonBar.
* @param left The String captions of the buttons to be placed on the left
* @param right The String captions of the buttons to be placed on the right
* @param orientation buttons orientation (HORIZONTAL, VERTICAL)
*/
public void setButtons(String[] left, String[] right, int orientation) {
this.orientation = orientation;
initButtons(left, right, ""); // NOI18N
invalidate();
repaint();
}
/** Adds the specified ButtonBar listener to receive ButtonBar events from
* this ButtonBar. ButtonBar events occur when a user clicks on one
* of the buttons on the ButtonBar.
* @param l the ButtonBar listener.
* @see #removeActionListener
*/
public synchronized void addButtonBarListener(ButtonBarListener l) {
listeners.addElement(l);
}
/** Removes the specified ButtonBar listener to receive ButtonBar events from
* this ButtonBar. ButtonBar events occur when a user clicks on one
* of the buttons on the ButtonBar.
* @param l the ButtonBar listener.
* @see #removeActionListener
*/
public synchronized void removeButtonBarListener(ButtonBarListener l) {
listeners.removeElement(l);
}
/** paints the button bar */
public void paint(Graphics g) {
Dimension d = getSize();
g.setColor(getBackground());
g.fillRect(0, 0, d.width, d.height);
super.paint(g); // because of the lightweight components
}
/** Returns a specified button's index. It is the index of the button from
* left to right. I.e. the leftmost button has index 0, and the rightmost
* has index <number of left buttons> + <number of right buttons> - 1
* @return a specified button's index
*/
public int getButtonIndex(ButtonBarButton button) {
if (button == null)
return -1;
for (int i=0; i<leftButtons.length; i++)
if (leftButtons[i].equals(button)) return i;
for (int i=0; i<rightButtons.length; i++)
if (rightButtons[i].equals(button)) return leftButtons.length + i;
return -1;
}
/** Get the button by its index */
public ButtonBarButton getButton(int index) {
if (index < 0 || index >= leftButtons.length + rightButtons.length)
return null;
return (index >= leftButtons.length) ?
rightButtons[index - leftButtons.length] : leftButtons[index];
}
/** Sets the default button by index */
public void setDefaultButton(int index) {
setDefaultButton(getButton(index));
}
/** Sets the default button in the ButtonBar. When 'Enter' key is pressed this
* button behaves the same way as it was pressed by mouse.
*/
public void setDefaultButton(final ButtonBarButton button) {
// if the peer doesn't exist just remember the default button
if (!peerExist) {
defaultButton = button;
return;
}
if (defaultButton != button) {
JButton oldDefault = defaultButton;
defaultButton = button;
Component c = getParent();
while(c != null) {
if (c instanceof JRootPane) {
/* now set the default button to root pane. However it
* will not work properly in text fields, so the next part
* patches the behavior.
*/
((JRootPane)c).setDefaultButton(button);
break;
}
c = c.getParent();
}
/* Because of the default button behavior we need to reregister
* action for VK_ENTER again using registerKeyboardAction() in root
* pane. If the root pane is not available we will register it for
* button bar directly. However in this case the button will not have
* the special border.
*/
if (c == null) { // if no root pane found
c = this; // register action for button bar
}
if (oldDefault != null) {
((JComponent)c).unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, true));
((JComponent)c).unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, false));
}
if (button != null) {
// common array for the two following registerKeyboardAction() calls
// acceptKeys[0] == true // start accepting keys
final boolean acceptKeys[] = new boolean[1];
acceptKeys[0] = true;
/* [PENDING] Patch for situation when the dialog with button bar and default button
* gets opened and the Enter key stays pressed. Since the registerKeyboardAction catches
* only release of Enter key, holding the Enter key would in this case cause
* default button action on Enter key release.
* /
Timer tm = new Timer(1000,
new ActionListener() {
public void actionPerformed(ActionEvent evt) {
acceptKeys[0] = true;
}
}
);
tm.setRepeats(false);
tm.start();
*/
((JComponent)c).registerKeyboardAction(
new ActionListener() {
public void actionPerformed(ActionEvent evt) {
if (button.isEnabled() && acceptKeys[0]) {
buttonPressed(button, 0);
}
}
},
KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, true),
WHEN_IN_FOCUSED_WINDOW
);
}
repaint();
}
}
/** This method notifies the actionListeners about that the give button has been pressed */
protected void buttonPressed(ButtonBarButton button, int modifiers) {
ButtonBarEvent evt = new ButtonBarEvent(this, button,
ActionEvent.ACTION_PERFORMED, button.getText(), modifiers);
Vector listeners2;
synchronized (this) {
listeners2 = (Vector)listeners.clone();
}
int length = listeners2.size ();
for (int i = 0; i < length; i++)
((ButtonBarListener)(listeners2.elementAt(i))).buttonPressed(evt);
}
public void addNotify() {
super.addNotify();
peerExist = true;
// now really set the default button
if (defaultButton != null) {
ButtonBarButton b = defaultButton;
defaultButton = null; // set function tests equality
setDefaultButton(b);
}
}
public void removeNotify() {
// disable default button
if (defaultButton != null) {
ButtonBarButton oldDefault = defaultButton;
setDefaultButton(null);
defaultButton = oldDefault;
}
super.removeNotify();
peerExist = false;
}
public final int getOrientation() {
return orientation;
}
/** The ButtonBarLayout class is a layout that lays the ButtonBarButtons according to their preferred size.
* The buttons in the left group are placed according to their preferred width from the left edge from first to
* the last and the buttons in the right group are placed according to their preferred size from the right edge
* from the last to the first.
*/
class ButtonBarLayout implements LayoutManager {
public void addLayoutComponent (String name, Component comp) {
}
public void removeLayoutComponent (Component comp) {
}
public Dimension preferredLayoutSize (Container parent) {
Dimension d = new Dimension(0, 0);
if ((leftButtons.length == 0) && (rightButtons.length == 0))
return d;
else {
Dimension maxButton = countMaxSize(leftButtons, rightButtons);
if (orientation == HORIZONTAL) {
d.height = maxButton.height;
d.width += maxButton.width * (leftButtons.length + rightButtons.length);
if ((leftButtons.length > 0) && (rightButtons.length > 0)) {
d.width += SECTIONS_GAP;
}
if (leftButtons.length > 1)
d.width += BUTTONS_GAP * (leftButtons.length - 1);
if (rightButtons.length > 1)
d.width += BUTTONS_GAP * (rightButtons.length - 1);
} else { // vertical orientation
d.height = maxButton.height * (leftButtons.length + rightButtons.length);
d.width += maxButton.width;
if ((leftButtons.length > 0) && (rightButtons.length > 0)) {
d.height += SECTIONS_GAP;
}
if (rightButtons.length > 1)
d.height += BUTTONS_GAP * (rightButtons.length - 1);
if (leftButtons.length > 1)
d.height += BUTTONS_GAP * (leftButtons.length - 1);
}
return d;
}
}
public Dimension minimumLayoutSize (Container parent) {
return preferredLayoutSize(parent);
}
public void layoutContainer (Container parent) {
if ((leftButtons.length == 0) && (rightButtons.length == 0))
return;
final Dimension size = parent.getSize();
final Dimension maxButton = countMaxSize(leftButtons, rightButtons);
int buttonsCount = leftButtons.length + rightButtons.length;
if (orientation == HORIZONTAL) {
if ((maxButton.width * buttonsCount) + (BUTTONS_GAP * (buttonsCount - 1)) > size.width)
maxButton.width = (size.width - (BUTTONS_GAP * (buttonsCount - 1))) / buttonsCount;
} else {
if ((maxButton.height * buttonsCount) + (BUTTONS_GAP * (buttonsCount - 1)) > size.height)
maxButton.height = (size.height - (BUTTONS_GAP * (buttonsCount - 1))) / buttonsCount;
}
final int butDelta = BUTTONS_GAP
+ ((orientation == HORIZONTAL) ? maxButton.width : maxButton.height);
if ((leftButtons.length > 0) && (rightButtons.length > 0)) { // both are not empty
if (orientation == HORIZONTAL) {
int x = 0;
for (int i = 0; i < leftButtons.length; i++) {
leftButtons[i].setBounds(new Rectangle(x, 0, maxButton.width, maxButton.height));
leftButtons[i].setSize(maxButton);
x += butDelta;
}
x = size.width - maxButton.width;
for (int i = rightButtons.length - 1; i >= 0; i--) {
rightButtons[i].setBounds(new Rectangle(x, 0, maxButton.width, maxButton.height));
rightButtons[i].setSize(maxButton);
x -= butDelta;
}
} else {
int y = 0;
for (int i = 0; i < rightButtons.length; i++) {
rightButtons[i].setBounds(new Rectangle(0, y, maxButton.width, maxButton.height));
rightButtons[i].setSize(maxButton);
y += butDelta;
}
y = size.height - maxButton.height;
for (int i = leftButtons.length - 1; i >= 0; i--) {
leftButtons[i].setBounds(new Rectangle(0, y, maxButton.width, maxButton.height));
leftButtons[i].setSize(maxButton);
y -= butDelta;
}
}
} else {
ButtonBarButton[] buts = (leftButtons.length > 0) ? leftButtons : rightButtons;
if (singleMode == SINGLE_CENTER) { // single centered
if (orientation == HORIZONTAL) { // horizontal orientation
int x = (size.width - (butDelta * buts.length - BUTTONS_GAP)) / 2;
for (int i = 0; i < buts.length; i++) {
buts[i].setBounds(new Rectangle(x, 0, maxButton.width, maxButton.height));
x += butDelta;
}
} else { // vertical orientation
int y = (size.height - (butDelta * buts.length - BUTTONS_GAP)) / 2;
for (int i = 0; i < buts.length; i++) {
buts[i].setBounds(new Rectangle(0, y, maxButton.width, maxButton.height));
y += butDelta;
}
}
} else if (singleMode == SINGLE_LEFT) { // single left
if (orientation == HORIZONTAL) { // horizontal orientation
int x = 0;
for (int i = 0; i < buts.length; i++) {
buts[i].setBounds(new Rectangle(x, 0, maxButton.width, maxButton.height));
buts[i].setSize(maxButton);
x += butDelta;
}
} else { // vertical orientation
int y = size.height - maxButton.height;
for (int i = buts.length - 1; i >= 0; i--) {
buts[i].setBounds(new Rectangle(0, y, maxButton.width, maxButton.height));
buts[i].setSize(maxButton);
y -= butDelta;
}
}
} else { // SINGLE_RIGHT
if (orientation == HORIZONTAL) { // horizontal orientation
int x = size.width - maxButton.width;
for (int i = buts.length - 1; i >= 0; i--) {
buts[i].setBounds(new Rectangle(x, 0, maxButton.width, maxButton.height));
buts[i].setSize(maxButton);
x -= butDelta;
}
} else { // vertical orientation
int y = 0;
for (int i = 0; i < buts.length; i++) {
buts[i].setBounds(new Rectangle(0, y, maxButton.width, maxButton.height));
buts[i].setSize(maxButton);
y += butDelta;
}
}
}
}
}
private Dimension countMaxSize(ButtonBarButton[] left, ButtonBarButton[] right) {
Dimension d1 = countMaxSize(left);
Dimension d2 = countMaxSize(right);
return new Dimension(Math.max(d1.width, d2.width), Math.max(d1.height, d2.height));
}
private Dimension countMaxSize(ButtonBarButton[] buttons) {
Dimension d = new Dimension(0, 0);
for (int i = 0; i < buttons.length; i++) {
Dimension d2 = buttons[i].getPreferredSize();
d.width = Math.max(d.width, d2.width);
d.height = Math.max(d.height, d2.height);
}
return d;
}
}
public static interface ButtonBarListener {
public void buttonPressed(ButtonBarEvent evt);
}
/** The ButtonBar event */
public static class ButtonBarEvent extends ActionEvent {
/** generated Serialized Version UID */
static final long serialVersionUID = 5361326502523278662L;
/** The button on which the event originated */
private ButtonBarButton sourceButton;
/** Constructs a ButtonBarEvent object with the specified source object
* (the ButtonBar) and sourceButton (ButtonBarButton).
* @param source the object where the event originated
* @param id the type of event
* @param command the command string for this action event
* @param modifiers the modifiers held down during this action
*/
public ButtonBarEvent(ButtonBar source, ButtonBarButton button, int id,
String command, int modifiers) {
super(source, id, command, modifiers);
sourceButton = button;
}
/** @return The button on which the event originated */
public ButtonBarButton getButton() {
return sourceButton;
}
}
/** the buttons */
private ButtonBarButton[] rightButtons, leftButtons;
/** the mode for single-size button alignment */
private int singleMode = SINGLE_RIGHT;
/** ButtonBar listeners
* @associates ButtonBarListener*/
private Vector listeners = new Vector();
}
/*
* Log
* 5 src-jtulach1.4 1/12/00 Ales Novak i18n
* 4 src-jtulach1.3 10/22/99 Ian Formanek NO SEMANTIC CHANGE - Sun
* Microsystems Copyright in File Comment
* 3 src-jtulach1.2 6/8/99 Ian Formanek ---- Package Change To
* org.openide ----
* 2 src-jtulach1.1 3/26/99 Ian Formanek Fixed use of obsoleted
* NbBundle.getBundle (this)
* 1 src-jtulach1.0 3/9/99 Jaroslav Tulach
* $
* Beta Change History:
* 0 Tuborg 0.15 --/--/98 Jan Formanek ButtonBarButton class moved out into separate class file
* 0 Tuborg 0.15 --/--/98 Jan Formanek Buttons identified by indexes
* 0 Tuborg 0.15 --/--/98 Jan Formanek Localization
* 0 Tuborg 0.16 --/--/98 Jan Formanek getPressedButton added
* 0 Tuborg 0.21 --/--/98 Jan Formanek empty button bar preset, default constuctor creates an
* 0 Tuborg 0.21 --/--/98 Jan Formanek empty button bar
* 0 Tuborg 0.30 --/--/98 Jan Formanek huge changes
* 0 Tuborg 0.32 --/--/98 Jan Formanek added setButtons(String[], String[])
* 0 Tuborg 0.33 --/--/98 Petr Hamernik default button in the predefined sets added.
* 0 Tuborg 0.34 --/--/98 Petr Hamernik layout rewritten
* 0 Tuborg 0.35 --/--/98 Jan Formanek added single-side alignment modes
*/